<?php
/**
 * This file defines the {@see \BF\Drawing\Rectangle} class.
 *
 * @author     Kado <ulfikado@gmail.com>
 * @category   Beluga PHP5.3+ Framework
 * @package    BF\Drawing
 * @since      2015-01-31 00:29
 * @subpackage Core
 * @version    0.1
 */

namespace BF\Drawing
{

   /**
    * Eine Klasse die ein 2-dimensionales Viereck, standortbezogen darstellen
    * kann.
    *
    * @since      v0.1
    * @property-read int $Right Die Position der rechten Kante des Rectangles,
    *             relativ zum Nullpunkt der aktuellen Position.
    * @property-read int $Bottom Die Position der unteren Kante des Rectangles,
    *             relativ zum Nullpunkt der aktuellen Position.
    * @property-read int $Left Alias für Eigenschaft 'X'
    * @property-read int $X Obere linke X-Koordinate des Rectangles
    * @property-read int $Top Alias für Eigenschaft 'Y'
    * @property-read int $Y Obere linke Y-Koordinate des Rectangles
    * @property-read int $Width Breite
    * @property-read int $Height Höhe
    */
   class Rectangle
   {

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   F I E L D S   - - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * The rectangle position.
       *
       * @var \BF\Drawing\Point
       */
      public $Point;

      /**
       * Größe des Rectangles
       *
       * @var \BF\Drawing\Size
       */
      public $Size;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - >   P U B L I C   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - -">

      /**
       * Inits a new instance.
       *
       * @param \BF\Drawing\Point $point
       * @param \BF\Drawing\Size  $size
       */
      public function __construct( \BF\Drawing\Point $point = null, \BF\Drawing\Size $size = null )
      {
         if ( ! \is_null( $point ) )
         {
            $this->Point = $point;
         }
         else
         {
            $this->Point = new \BF\Drawing\Point();
         }
         if ( ! \is_null( $size ) )
         {
            $this->Size = $size;
         }
         else
         {
            $this->Size = new \BF\Drawing\Size();
         }
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Returns the X position of the right rectangle corner.
       *
       * @return integer
       */
      public final function getRight()
      {
         return $this->Point->X + $this->Size->Width;
      }

      /**
       * Returns the Y position of the bottom rectangle corner.
       *
       * @return integer
       */
      public final function getBottom()
      {
         return $this->Point->Y + $this->Size->Height;
      }

      /**
       * Returns, if the current rectangle contains the defined rectangle.
       *
       * @param  \BF\Drawing\Rectangle $rect
       * @return boolean
       */
      public final function contains( \BF\Drawing\Rectangle $rect )
      {
         return (
            (
               (
                  (
                     $this->Point->X <= $rect->Point->X
                  )
               && (
                     ( $rect->Point->X + $rect->Size->Width ) <= ( $this->Point->X + $this->Size->Width )
                  )
               )
            &&
               (
                  $this->Point->Y <= $rect->Point->Y
               )
            )
         &&
            (
               ( $rect->Point->Y + $rect->Size->Height ) <= ( $this->Point->Y + $this->Size->Height )
            )
         );
      }

      /**
       * Returns, if the current rectangle contains the defined location.
       *
       * @param  int|\BF\Drawing\Point $xOrPoint
       * @param  int|null              $y
       * @return boolean
       */
      public final function containsLocation( $xOrPoint, $y = null )
      {
         $x = $xOrPoint;
         if ( $x instanceof \BF\Drawing\Point )
         {
            $y = $x->Y;
            $x = $x->X;
         }
         elseif ( \is_integer( $xOrPoint ) )
         {
            $x = $xOrPoint;
         }
         elseif ( \BF\TypeTool::IsInteger( $xOrPoint ) )
         {
            $x = \intval( $xOrPoint );
         }
         else
         {
            $x = 0;
         }
         if ( ! \is_int( $y ) )
         {
            $y = \intval( $y );
         }
         return (
               (
                  (
                     ( $this->Point->X <= $x )
                  &&
                     ( $x < ( $this->Point->X + $this->Size->Width ) )
                  )
               &&
                  ( $this->Point->Y <= $y )
               )
            &&
               ( $y < ( $this->Point->Y + $this->Size->Height ) )
         );
      }

      /**
       * Returns, if the current rectangle contains the defined Size.
       *
       * @param  \BF\Drawing\Size|integer $widthOrSize
       * @param  integer                  $height
       * @return boolean
       */
      public final function containsSize( $widthOrSize, $height = null )
      {
         $width = $widthOrSize;
         if ( $width instanceof \BF\Drawing\Size )
         {
            $height = $width->Height;
            $width  = $width->Width;
         }
         elseif ( \is_integer( $widthOrSize ) )
         {
            $width = $widthOrSize;
         }
         elseif ( \BF\TypeTool::IsInteger( $widthOrSize ) )
         {
            $width = \intval( $widthOrSize );
         }
         else
         {
            $width = 0;
         }
         if ( ! \is_int( $height ) )
         {
            $height = \intval( $height );
         }
         return
               $this->Size->Width  >= $width
            &&
               $this->Size->Height >= $height;
      }

      /**
       * Gets a clone of the current instance.
       *
       * @return \BF\Drawing\Rectangle
       */
      public function getClone()
      {
         return new \BF\Drawing\Rectangle(
            new \BF\Drawing\Point(
               $this->Point->X,
               $this->Point->Y
            ),
            new \BF\Drawing\Size(
               $this->Size->Width,
               $this->Size->Height
            )
         );
      }

      public function __clone()
      {
         return $this->getClone();
      }

      /**
       * Builds the intersection (Schnittmenge) between the current and the defined rectangle.
       *
       * @param  \BF\Drawing\Rectangle $rect
       * @return \BF\Drawing\Rectangle Returns the resulting rectangle or boolean FALSE if no intersection exists.
       */
      public final function intersect( \BF\Drawing\Rectangle $rect )
      {
         $x = \max( $this->Point->X, $rect->Point->X );
         $num2 = \min(
            ( $this->Point->X + $this->Size->Width ),
            ( $rect->Point->X + $rect->Size->Width )
         );
         $y = \max( $this->Point->Y, $rect->Point->Y );
         $num4 = \min(
            ( $this->Point->Y + $this->Size->Height ),
            ( $rect->Point->Y + $rect->Size->Height )
         );
         if ( ( $num2 >= $x ) && ( $num4 >= $y ) )
         {
            return \BF\Drawing\Rectangle::Init(
               $x,
               $y,
               $num2 - $x,
               $num4 - $y
            );
         }
         else
         {
            return false;
         }
      }

      /**
       * Returns if the current rectangle is a empty rectangle (width, height, x and y must be lower than 1)
       *
       * @return boolean
       */
      public final function isEmpty()
      {
         return $this->Point->isEmpty()
            &&  $this->Size->Width  < 1
            &&  $this->Size->Height < 1;
      }

      /**
       * Builds a new rectangle that must contain the current rectangle and the defined rectangle. (a union)
       *
       * @param  \BF\Drawing\Rectangle $rect
       * @return \BF\Drawing\Rectangle Returns the resulting rectangle.
       */
      public final function union( \BF\Drawing\Rectangle $rect )
      {
         $x = \max( $this->Point->X, $rect->Point->X );
         $num2 = \max(
            $this->Point->X + $this->Size->Width,
            $rect->Point->X + $rect->Size->Width
         );
         $y = \min( $this->Point->Y, $rect->Point->Y );
         $num4 = \max(
            $this->Point->Y + $this->Size->Height,
            $rect->Point->Y + $rect->Size->Height
         );
         return \BF\Drawing\Rectangle::Init(
            $x,
            $y,
            $num2 - $x,
            $num4 - $y
         );
      }

      public function  __get( $name )
      {

         switch ( \strtolower( $name ) )
         {

            case 'left':
            case 'x':
               return $this->Point->X;

            case 'top':
            case 'y':
               return $this->Point->Y;

            case 'width':
            case 'w':
               return $this->Size->Width;

            case 'height':
            case 'h':
               return $this->Size->Height;

            case 'bottom':
               return $this->getBottom();

            case 'right':
               return $this->getRight();

            default:
               return false;

         }

      }

      /**
       * Writes the current instance data as XML element to a XmlWriter.
       *
       * The resulting XML element looks like follow:
       *
       * <b>&lt;Rectangle x="0" y="0" width="0" height="0"/&gt;</b>
       *
       * @param \XMLWriter $w           The XMLWRiter.
       * @param string     $elementName The name of the resulting XML element.  (default='Rectangle')
       *                                If no usable element name is defined, only the attributes are written!
       */
      public function writeXml( \XMLWriter $w, $elementName = 'Rectangle' )
      {
         $writeElement = ! empty( $elementName );
         if ( $writeElement )
         {
            $w->startElement( $elementName );
         }
         $this->writeXMLAttributes( $w );
         if ( $writeElement )
         {
            $w->endElement();
         }
      }

      /**
       * Write the current instance data as XML element attributes (width, height, x, y) to defined XmlWriter.
       *
       * @param \XMLWriter $w The XMLWRiter.
       */
      public function writeXmlAttributes( \XMLWriter $w )
      {
         $this->Point->writeXmlAttributes( $w );
         $this->Size->writeXmlAttributes( $w );
      }

      /**
       * Returns the rectangle string. Format is: "width=?; height=?; x=?; y=?"
       *
       * @return string
       */
      public function __toString()
      {
         return (string) $this->Point . '; ' . (string) $this->Size;
      }

      /**
       * Returns a array with all instance data. Used array keys are 'x', 'y', 'width' and 'height'.
       *
       * @return array
       */
      public function toArray()
      {
         return array(
            'x'      => $this->Point->X,
            'y'      => $this->Point->Y,
            'width'  => $this->Size->Width,
            'height' => $this->Size->Height
         );
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - - -">

      /**
       * …
       *
       * @param  integer $x
       * @param  integer $y
       * @param  integer $width
       * @param  integer $height
       * @return \BF\Drawing\Rectangle
       */
      public static function Init( $x, $y, $width, $height )
      {
         return new \BF\Drawing\Rectangle(
            new \BF\Drawing\Point( $x, $y ),
            new \BF\Drawing\Size( $width, $height )
         );
      }

      /**
       * …
       *
       * @param resource $imageResource
       * @return \BF\Drawing\Rectangle
       */
      public static function FromImageResource( $imageResource )
      {
         return new \BF\Drawing\Rectangle(
            new \BF\Drawing\Point(),
            new \BF\Drawing\Size( \imagesx( $imageResource ), \imagesy( $imageResource ) )
         );
      }

      /**
       * Parses a XML element to a {@see \BF\Drawing\Rectangle} instance.
       *
       * @param  \SimpleXMLElement $element Das XML-Element mit den Instanzdaten
       * @return \BF\Drawing\Rectangle or boolean FALSE
       */
      public static function FromXml( \SimpleXMLElement $element )
      {
         $point = null;
         if ( ! \BF\Drawing\Point::TryParse( $element, $point ) )
         {
            return false;
         }
         $size = null;
         if ( !\BF\Drawing\Size::TryParse( $element, $size ) )
         {
            return false;
         }
         return new \BF\Drawing\Rectangle(
            $point,
            $size
         );
      }

      /**
       * Parses a string to a {@see \BF\Drawing\Rectangle} instance.
       *
       * Accepted string must use the following format:
       *
       * <code>x=0; y=0; width=800; height=600</code>
       *
       * @param  string $objectString Zeichenkette die die Daten einer
       *                Instanz des implementierenden Objekts entspricht.
       * @return \BF\Drawing\Rectangle Or boolean FALSE
       */
      public static function FromString( $objectString )
      {
         $hits = null;
         if ( ! \preg_match( '~^x=(\d{1,4});\s*y=(\d{1,4});\s*width=(\d{1,4});\s*height=(\d{1,4})$~',
                             $objectString, $hits ) )
         {
            return false;
         }
         return self::Init(
            \intval( $hits[ 1 ] ),
            \intval( $hits[ 2 ] ),
            \intval( $hits[ 3 ] ),
            \intval( $hits[ 4 ] )
         );
      }

      /**
       * Parses a array to a {@see \BF\Drawing\Rectangle} instance.
       *
       * Required array keys are: x, y, width, height
       *
       * @param  array $objectData
       * @return \BF\Drawing\Rectangle Or boolean FALSE
       */
      public static function FromArray( array $objectData )
      {
         $objectData = \array_change_key_case( $objectData );
         if ( isset( $objectData[ 'x' ] )
           && isset( $objectData[ 'y' ] )
           && isset( $objectData[ 'width' ] )
           && isset( $objectData[ 'height' ] ) )
         {
            return self::Init(
               \intval( $objectData['x'] ),
               \intval( $objectData['y'] ),
               \intval( $objectData['width'] ),
               \intval( $objectData['height'] )
            );
         }
         return false;
      }

      /**
       * Parses a array to a {@see \BF\Drawing\Rectangle} instance. Required format "x=?; y=?; width=?; height=?"
       *
       * @param  string $str
       * @return \BF\Drawing\Rectangle Or boolean FALSE
       */
      public static function TryParse( $value, &$output )
      {
         if ( $value instanceof \BF\Drawing\Rectangle )
         {
            $output = $value;
            return true;
         }
         if ( \is_string( $value ) )
         {
            return ( false !== ( $output = self::FromString( $value ) ) );
         }
         if ( \is_array( $value ) )
         {
            return ( false !== ( $output = self::FromArray( $value ) ) );
         }
         if ( $value instanceof \SimpleXMLElement )
         {
            return ( false !== ( $output = self::FromXml( $value ) ) );
         }
         return false;
      }

      # </editor-fold>

   }

}

